package com.ssm.security;

import com.ssm.entity.AppUser;
import com.ssm.entity.UserProfile;
import com.ssm.mapper.AppUserMapper;
import com.ssm.mapper.UserProfileMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;

//这里必须加上@Service以供security.xml加载
@Service
public class MyUserDetailService implements UserDetailsManager,UserDetailsService{

	protected final Log logger = LogFactory.getLog(this.getClass());
	private  Map<String, MutableUserDetails> users = new HashMap<>();

	@Autowired
	private AppUserMapper appUserMapper;

	@Autowired
	private UserProfileMapper userProfileMapper;

	@Autowired
	private AuthenticationManager authenticationManager;

	/**
	 * 根据用户名获取-单张表-的权限密码等信息
	 * UserDetails只是一个接口，所以返回的对象只要继承了这个接口就可以，给返回去的对象可以进行check就行，所以此处返回的对象可以自定义，以便生成token需要使用的对象属性。
	 * @param username
	 * @return
	 * @throws org.springframework.security.core.userdetails.UsernameNotFoundException
	 */
	@Override
	public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {

		AppUser user = null;
		try {
			user = appUserMapper.selectBySSO(username);
		} catch (UsernameNotFoundException e) {
			throw new  UsernameNotFoundException("loadUser failed check the database connect");
		}
		if(user==null)
		{
			throw new  UsernameNotFoundException("UsernameNotFound");
		}
		boolean enables = true;
		boolean accountNonExpired = true;
		boolean credentialsNonExpired = true;
		boolean accountNonLocked = true;
		Collection<GrantedAuthority> grantedAuths = getAuthorities(user);
		JWTUserDetails jwtUserDetails = new JWTUserDetails(user.getId(),user.getFirstName(),user.getPassword(),enables,accountNonExpired,credentialsNonExpired,accountNonLocked,grantedAuths);
		return  jwtUserDetails;
	}

	/**
	 * 根据用户ID遍历出该用户的所有角色信息放入collection
	 * @param user
	 * @return
	 */
	private Collection<GrantedAuthority> getAuthorities(AppUser user) {
		Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
		/*GrantedAuthority是赋予到主体的权限。？？？*/
		Set<UserProfile> userProfiles= userProfileMapper.getUserRoleAllByUserId(user.getId());
		for (UserProfile userProfile : userProfiles){
			SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(userProfile.getType());
			grantedAuthorities.add(grantedAuthority);
		}
		return grantedAuthorities;
	}

	@Override
	public void createUser(UserDetails userDetails) {
		Assert.isTrue(!this.userExists(userDetails.getUsername()));
		this.users.put(userDetails.getUsername().toLowerCase(), new MutableUser(userDetails));
	}

	@Override
	public void updateUser(UserDetails userDetails) {
		Assert.isTrue(!this.userExists(userDetails.getUsername()));
		this.users.put(userDetails.getUsername().toLowerCase(), new MutableUser(userDetails));
	}

	@Override
	public void deleteUser(String s) {
		this.users.remove(s.toLowerCase());
	}

	/**
	 * 修改基于内存的凭证密码
	 * @param oldPassword
	 * @param newPassword
	 */
	@Override
	public void changePassword(String oldPassword, String newPassword) {
		Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
		if(currentUser == null) {
			throw new AccessDeniedException("Can\'t change password as no Authentication object found in context for current user.");
		} else {

			String username = currentUser.getName();
			UserDetails user1 = loadUserByUsername(username);
			deleteUser(username);//在下面createUser之前先要删除当前的MutableUser，否则在第二次修改的时候会出错
			this.createUser(user1);//在下面MutableUserDetails设置密码前必须先创建用户
			this.logger.debug("Changing password for user \'" + username + "\'");
			if(this.authenticationManager != null) {
				this.logger.debug("Reauthenticating user \'" + username + "\' for password change request.");
				this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, oldPassword));
			} else {
				this.logger.debug("No authentication manager set. Password won\'t be re-checked.");
			}

			MutableUserDetails user = users.get(username);
			if(user == null) {
				throw new IllegalStateException("Current user doesn\'t exist in database.");
			} else {
				user.setPassword(newPassword);
			}
		}
	}

	@Override
	public boolean userExists(String s) {
		return this.users.containsKey(s.toLowerCase());
	}

	public void setAuthenticationManager(AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}
}
